Skip to content

feat(i18n): add Traditional Chinese locale - #3853

Open
AcerYue0 wants to merge 9 commits into
apache:mainfrom
AcerYue0:feat/zh-tw-locale
Open

feat(i18n): add Traditional Chinese locale#3853
AcerYue0 wants to merge 9 commits into
apache:mainfrom
AcerYue0:feat/zh-tw-locale

Conversation

@AcerYue0

@AcerYue0 AcerYue0 commented Aug 26, 2026

Copy link
Copy Markdown

Summary

Add separate zh-CN and zh-TW resolved locales, including Taiwan-localized UI catalogs and system-locale aliases, while preserving the former persisted zh preference as zh-CN.

Replace the Desktop Settings language segmented control with the existing @maka/ui Selector, offering Follow system, Simplified Chinese, Traditional Chinese, and English.

Verification

  • npm run build — passed
  • npm run lint — passed
  • npm run format:check — passed
  • npm run typecheck — passed
  • npx knip --workspace apps/desktop — passed
  • npx knip --workspace packages/ui — passed
  • npm --workspace @maka/core test — 685 passed
  • npm --workspace @maka/ui test — 231 passed
  • macOS screenshot workflow (Traditional Chinese locale screenshots) — added; runs on macos-15 and uploads real Settings screenshots as an artifact
  • Desktop locale-focused regression suites — passed
  • CLI locale-focused suites — passed

Review follow-up

  • Preserve canonical zh-CN / zh-TW E2E fixture flags.
  • Carry the resolved locale through startup dialogs and persisted Desktop locale preferences.
  • Keep raw Simplified backend text out of zh-TW surfaces, including Health Center and GitHub Copilot failures.
  • Add explicit Taiwan copy for non-catalog fallbacks such as Agent Graph, WorkHub, MCP, readiness, notifications, and native dialogs.

Screenshots

  • Add before/after screenshots from a real macOS Desktop Settings UI (the workflow in this PR produces the artifact)

This PR remains a draft until the required real UI screenshots are attached.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: ChatGPT implemented the locale contract, Traditional Chinese catalogs, Settings selector change, review fixes, and related tests.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

@AcerYue0
AcerYue0 marked this pull request as ready for review August 26, 2026 03:52

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this head and found blocking i18n issues.

[P2] zh-TW locale mishandled — three places

  • e2e-fixture.ts:119-123 lowercases locale then compares to zh-CN/zh-TW → both become null.
  • runtime-host-boot.ts:1629-1648 passes zh-CN for zh-TW systems.
  • Multiple UI fallbacks use locale!=='en' to pick zh-CN strings, sending Simplified to Traditional users.

Fix: preserve zh-TW throughout and fix comparisons/fallbacks.

简体中文繁中语言支持被错误归入简中。

Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

@M4n5ter
M4n5ter force-pushed the feat/zh-tw-locale branch from bf8fe16 to 4b56a8c Compare August 26, 2026 08:51
@M4n5ter
M4n5ter force-pushed the feat/zh-tw-locale branch from 4b2ce5e to 5b8f9db Compare August 26, 2026 09:57
@AcerYue0
AcerYue0 marked this pull request as draft August 26, 2026 10:07
@AcerYue0
AcerYue0 marked this pull request as ready for review August 26, 2026 10:08
@AcerYue0

AcerYue0 commented Aug 27, 2026

Copy link
Copy Markdown
Author

Addressed the blocking i18n findings in 5b8f9dbc6, with additional latest-main follow-ups through 13428763b.

  • Canonicalized the E2E fixture values after lowercasing, preserving both zh-CN and zh-TW.
  • Carried the resolved locale through startup recovery dialogs; post-settings prompts now use the persisted Desktop locale.
  • Replaced Simplified-only fallbacks with explicit zh-TW copy or locale-aware sanitization across error classification, Agent Graph, WorkHub, MCP, readiness, notifications, native dialogs, Health Center, and GitHub Copilot failures.
  • Added regression coverage for the reviewed paths and additional raw-backend-message boundaries.
  • Synced with the latest apache/main locale surfaces, including MCP TUI status, new app icons, Runtime Host directory controls, WorkHub failures, and usage labels.
  • Replaced remaining Mainland-only provider/bot terminology and sanitized raw Work Board, Runtime Host profile, updater, onboarding, and memory-open errors.
  • Completed a catalog-wide Taiwan terminology pass, including 取得, 存取, 權杖, 串接, 管道, 符合, and 記錄.
  • Rebasing onto the latest apache/maka:main resolved the UsageLongTail Story conflict by retaining the upstream usage assertions and the locale-aware copy selection.
  • Added the latest upstream Runtime Host Direct peer and usage-summary fields to the Traditional Chinese catalogs; full typecheck is green again.
  • Reorganized the history so 04d1ba082 is the earliest focused core locale commit, followed by the catalog and Desktop implementation commits; the rewritten tree is identical to the pre-rewrite tree.

Local verification now passes: build, lint, format, full typecheck, Core (685), UI (231), Desktop locale-focused suites, and CLI locale suites.

Real macOS before/after screenshots are still pending as noted in the PR body; the screenshot workflow is included in 13428763b.

@me2seeks

Copy link
Copy Markdown
Contributor

Hi @AcerYue0 — first off, huge thanks for taking on the Traditional Chinese locale! This is the first real proof that #2672's typed UiCatalog contract actually scales, and #3974 (Korean) is already reusing your shape.

Just a quick, totally non-blocking thought since you're still in draft (4282 ++ / 578 --, 100 files, effort/XL) — have you considered splitting this into a couple of smaller slices if review starts feeling heavy?

For example, the shape we just sketched for Korean might also fit here and keep each review under ~30 files:

  1. coreUI_LOCALES / resolveSystemUiLocale / uiLocaleToIntlLocale + desktop-locale-authority / fixture canonicalization
  2. desktop catalogs — the apps/desktop/src/renderer/locales/*.ts batch (could be 2 slices: settings-* vs conversation/artifact/browser/mcp)
  3. native + e2e + workflowruntime-host-*copy / notifications / zh-tw-locale-screenshots.yml + screenshot artifact

No pressure at all to rework — you've already addressed the blocking i18n findings in 5b8f9dbc6 and the follow-ups, and keeping it as one PR is also perfectly fine if you prefer. Just wanted to float it in case it helps reviewers (and helps us avoid a big merge queue).

On that note — we just opened #3974 + six i18n-ko slices (#3975#3980) which will touch the same central file (packages/core/src/ui-locale.ts) and many of the same *-copy.ts files. My suggestion would be:

Totally your call — just wanted to coordinate early so we don't step on each other's UiCatalog edits. Happy to help rebase either way!

Thanks again for the awesome work here 🙏

@AcerYue0

Copy link
Copy Markdown
Author

Thanks for the suggestion — I agree that the core-first split has real coordination value here, especially because the Korean work can reuse the locale contract once it lands.

I plan to restructure this as two stacked slices:

  1. A small core slice containing the resolved locale model (UI_LOCALES, system resolution, uiLocaleToIntlLocale), persisted zh migration, and the related locale-authority/fixture tests.
  2. This follow-up Desktop slice containing the typed zh-TW catalogs, shared UI/CLI/native surfaces, E2E coverage, and the macOS screenshot workflow.

I will keep the second slice based on the core slice, remove duplicated core changes from this PR, and rebase it onto apache/maka:main after the core slice lands. This should leave the Korean core slice a clean path to reuse the same contract while avoiding six overly granular PRs for the remaining UI work.

I will first resolve the current upstream story conflict as part of the rebase, then post the new core PR link and updated dependency relationship here.

@AcerYue0

Copy link
Copy Markdown
Author

Follow-up after checking the current type contract: UiCatalog<T> is a strict Record<UiLocale, T>, so adding zh-TW requires every catalog to carry the new key immediately. A truly independent, green core-only PR with no catalog changes is therefore not possible without weakening the type contract or introducing temporary Simplified fallbacks.

I will preserve the type-safe, fully localized implementation and avoid a non-green or placeholder core PR. The safe delivery shape is to keep this as one mergeable feature PR with the core changes clearly separated in commit history; if maintainers prefer a stacked split despite that coupling, I can instead prepare a core-plus-catalog-shape slice and keep the localized surfaces as the follow-up.

@AcerYue0

AcerYue0 commented Aug 27, 2026

Copy link
Copy Markdown
Author

@me2seeks
One locale-contract question before restructuring the branches: the Korean plan sketches the existing resolved locales as ['zh', 'en'] and says existing zh behavior should remain unchanged, while this PR intentionally changes the resolved contract to exactly ['zh-CN', 'zh-TW', 'en'].

Which direction should the shared contract take upstream?

  • Keep resolved Simplified Chinese as zh, with uiLocaleToIntlLocale('zh') === 'zh-CN', and add zh-TW alongside it; or
  • Adopt the region-aware contract from this PR, with zh-CN and zh-TW, and have the Korean slice build on ['zh-CN', 'zh-TW', 'en'].

This affects UiLocale, persisted-preference migration, every strict UiCatalog<T>, and the proposed Korean core slice, so I would like to confirm the canonical shape before rewriting the stacked branches.

@me2seeks

Copy link
Copy Markdown
Contributor

Hi @AcerYue0 — thanks so much for laying this out so clearly, and for the two thoughtful follow-ups!

We've talked it through and here's what we'd love to go with:

Contract: let's go with B — ['zh-CN','zh-TW','en','ko']. You're right that the Korean plan still sketched ['zh','en'], so let's treat that as outdated. Your migration (zhzh-CN) and the strict Record<UiLocale, T> shape you already landed are exactly the clean, region-aware contract we want long-term (zh-CN vs zh-TW is much clearer than a generic zh). We'll update the Korean tracking (#3974 / #3975) to build on ['zh-CN','zh-TW','en'] — please don't rework anything on your side for this.

Delivery: let's keep your single mergeable PR with the core history clearly separated — no need to truly split. You nailed the constraint: UiCatalog<T> is strict, so a genuinely green core-only PR would need placeholder copy and would just bring back the Simplified-leakage you just fixed. Keeping one PR but with a clean core commit at the bottom gives us the review benefit without weakening types. Totally happy with this.

That also makes the parallel work super simple: we'll park the Korean core slice (#3975) right behind you and rebase once yours lands — just one line in UI_LOCALES to ['zh-CN','zh-TW','en','ko'], everything else is additive, so no real conflict. If there's anything we can do to help with the rebase or the story conflict you mentioned, just ping us.

Thanks again for the amazing work and for catching the contract question early — really appreciate it!

@AcerYue0
AcerYue0 marked this pull request as ready for review August 27, 2026 07:11
@AcerYue0

Copy link
Copy Markdown
Author

@me2seeks Update: I reorganized the commit history as discussed while keeping this as one mergeable PR.

  • 04d1ba082 is now the earliest focused core locale commit (UI_LOCALES, resolver, uiLocaleToIntlLocale, persisted zh migration, and core tests).
  • The following commits contain the typed zh-TW catalogs and Desktop/shared/native/CLI/E2E implementation.
  • The rewritten tree is identical to the pre-rewrite tree; no feature content was dropped.
  • Rebase conflict in settings-pages.stories.tsx is resolved.
  • Build, typecheck, and format check pass.

The PR head is now 13428763b and GitHub reports it as mergeable. The locale contract remains ['zh-CN', 'zh-TW', 'en'], so the Korean core slice can add ko after this lands.

@Astro-Han

Copy link
Copy Markdown
Contributor

This is a synthesis of five independent blind reviews at exact head 13428763bafa9914b2dfb8133d6c73551eaeb363 (base 3eee0bd18af4263ec30e9ccc75b8a6f7b8a9680e, 175 files +4300/-579). I verified the diff and the exact-head state myself; the file:line findings below are from each reviewer's sealed report.

What I checked myself:

  • Read gh pr diff 3853 for head 1342876 and confirmed the cross-client scope (Core + shared UI + Desktop + CLI/TUI) and the atomically-added zh-TW catalog (2941 lines catalog, 671 lines tests, 688 lines infra).
  • Checked the merge-base 3eee0bd1 → 1342876 and that the PR is OPEN/non-draft/MERGEABLE/BLOCKED/REVIEW_REQUIRED (no new hosted test run for this head was found — the head was force-pushed without a test dispatch).

Findings per lane (file:line anchored):

#385 Core locale contract & legacy zh migration — GO, 1×P3 + 2 observations (Ox)

  • P3 — MAKA_LOCALE=zh regresses from accepted to hard errorpackages/cli/src/cli-ui-locale.ts:36,563-567 now exits 2 with “Expected zh-CN, zh-TW, en, or auto”, while packages/core/src/settings.ts:886-893 preserves the former generic zh as zh-CN on read. The env surface contradicts the persisted-settings migration. Accept zh (and optionally POSIX zh_CN/zh_TW) as aliases mapping to zh-CN/zh-TW.
  • Observation — storage/settings-store.ts:116 stores without normalizing, so an old client can rewrite zh to disk, but each read re-migrates deterministically — no drift.
  • Observation — ui-locale.ts:49/53 delimiter asymmetry ([-.] vs [-_]) after replaceAll('_','-') — sensibility only.
  • Verified: base MAKA_LOCALE=zh → zh, head zh → error; zh-CN/zh-TW/ZH-cn pass; 681/681 Core tests green.

#386 Desktop locale authority & persistence boundary — CODE NO-GO, 1×P2 (Luna-Deep)

  • P2 — Importing a legacy zh config causes authority to serve a non-existent locale and crashes native recoverypackages/runtime-host/src/operator/config-transfer-service.ts:103-105 passes old personalization.uiLocale: 'zh' directly to settingsStore.update (settings.ts:718-746, storage/settings-store.ts:112-120); settings.ts:877-892 only normalizes on read, so runtime-host-settings-ipc-main.ts:138-148 → client-settings-effects.ts:65-87 → desktop-locale-authority.ts:66-69 can project zh with no validation. If the renderer crashes at that moment, runtime-host-boot.ts:366-371 → native-diagnostic-dialog.ts:97-112 does MAIN_RENDERER_GONE_COPY['zh'].detailTypeError: Cannot read properties of undefined, and main-window.ts:389-395 only app.quit — the recovery dialog never appears. Reproduced: raw zh → read zh-CN → authority zh → native copy throws.

#387 zh-TW copy & fallback coverage — GO (conditional), 2×P3 (DSv4F)

  • P3-1 — Simplified-character residuepackages/ui/src/scheduled-task-copy.ts:218 countdown days: (count) => \${count} 天后`should be天後; same block uses elsewhere. Consumerscheduled-task-helpers.ts:236 → panel.tsx:545/586`.
  • P3-2 — Term inconsistency — same block weeks: (count) => \${count} 周後`should be週後; the block uses 週` seven times (每週, 週末…).
  • Fallback has no gap: UiLocale = 'zh-CN'|'zh-TW'|'en' at core/ui-locale.ts:29 (Record<UiLocale,T>) forces catalog completeness; resolveSystemUiLocale 38-44, resolveUiLocale 52-58, settings.ts:886 fail-closed, locale-context.tsx:62-68 fail-fast, status-item.ts:213 COPY[locale] ?? COPY.en, 64 locale sources all have zh-TW (provider-display-copy 61/61).
  • Scanned exclusions (conversion tables, zh() selector, zh-CN branch, test fixtures) are not residues. Verified: Core 689/689, CLI locale 11/11, conversation-copy 2/2.

#388 E2E fixture & screenshot workflow — GO (Muse-Reviewer)

#389 Feature necessity & convergence — FEATURE NECESSITY GO / LARGE-DIFF RETAIN / GLOBAL-DESIGN CONDITIONAL GO, not merge-ready (Feature)

  • 175 files is not scope loss: +4300/-579; 2941 lines (68.4%) catalog, 671 (15.6%) tests/workflow, 688 (16.0%) infra. UiCatalog<T> = Record<UiLocale,T> requires the whole catalog to be added atomically; splitting Core from catalog only yields a red typecheck or temporary fallback.
  • Four blockers to a unconditional GO (all must converge):
    1. Compatpackages/cli/src/cli-ui-locale.ts:35-47 drops the previously-tested MAKA_LOCALE=zh — canonicalize it to zh-CN as for persisted settings (same as refactor(desktop): split main process orchestration #385 P3).
    2. Permanent one-time CI.github/workflows/zh-tw-locale-screenshots.yml:1-44 starts a 45-minute macos-15 job on every Desktop/Core/UI PR; four runs so far are action_required and have produced no screenshots. Take real screenshots, then delete the workflow or make it workflow_dispatch only; keep the spec if it is a stable smoke test.
    3. Classifier authority duplicationpackages/core/src/redaction.ts:211-292 duplicates timeout/rate/auth/5xx/network classification across three language functions (English/Simplified/Traditional). Collapse to single “classify once → typed locale catalog” routing; the fourth language (Korean, feat(i18n-ko): core — add ko to UiLocale model #3975) would otherwise become a fourth copy. The widen-then-tighten Partial<Record<UiLocale,T>> & Record<'en',T> for Korean in feat(i18n-ko): core — add ko to UiLocale model #3975 should be rejected.
    4. Delivery-contract mismatch — the title says Desktop, the patch spans Core/shared UI/Desktop/CLI/TUI; the body says “remain Draft until screenshots” but GitHub shows Ready with no screenshots. Align title/body and Draft state with the real cross-client i18n scope.

What I did not judge:

  • E2E screenshot workflows were not executed (needs macOS/Playwright); locator correctness was inspected statically (zh-TW segment uses traditional characters).
  • A full cross-strait terminology audit beyond the residue scan was not performed.

Gate: Standards 1×P2 (authority crash, #386) + Spec CONDITIONAL GO (four #389 blockers) + 3×P3 copy fixes remain; hosted test for this exact head has not been dispatched. The head is not merge-ready until the legacy zh import is coerced and locale-aware recovery is proven, the MAKA_LOCALE=zh alias is restored, and the screenshot workflow/classifier/title-scope items are converged. No purely-rebased prior approval can be reused.


Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

@AcerYue0 AcerYue0 changed the title feat(desktop): add Traditional Chinese locale feat(i18n): add Traditional Chinese locale Aug 27, 2026
@AcerYue0
AcerYue0 requested a review from Astro-Han August 27, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/XL Over 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants